package com.oa.server;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @version 1.0
 * @author xie
 * @Description: 原生 Socket 实现聊天服务
 * @date: 2025-03-05 10:34
 */
@Component
@ServerEndpoint("/SocketTest/{sid}")
public class SocketTest {
    // 记录当前在线的 WebSocket 连接数
    private static int onlineCount = 0;
    // 存储所有 WebSocket 连接的集合，使用线程安全的 CopyOnWriteArraySet
    private static CopyOnWriteArraySet<SocketTest> webSocketSet = new CopyOnWriteArraySet<>();
    // 维护群聊映射，Key 为群聊 ID，Value 为该群聊中的用户 ID 集合  群聊中有哪些用户
    private static ConcurrentHashMap<String, Set<String>> groupChats = new ConcurrentHashMap<>();
    //存储每个用户加入的群聊
    private static ConcurrentHashMap<String, Set<String>> userGroups = new ConcurrentHashMap<>();
    // 当前 WebSocket 连接的会话对象，用于与客户端通信
    private Session session;
    // 当前连接的唯一标识符 (用户 ID)
    private String sid = "";
    // Redis 服务器的主机地址
    private static final String host = "192.168.109.130";
    // Redis 服务器的端口号
    private static final int port = 6379;
    // 连接 Redis 服务器的密码
    private static final String pwd = "123456";


    /**
     * 获取当前发消息的时间
     * @return yyyy-MM-dd HH:mm
     */
    public static String getCurrentTime() {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        return LocalDateTime.now().format(formatter);
    }

    /*
     * 判断消息是否为图片
     */
    private static boolean isImage(String message) {
        return message.endsWith(".jpg") || message.endsWith(".jpeg") || message.endsWith(".png") || message.endsWith(".gif");
    }

    /**
     * 获取socket连接
     * @param session 会话
     * @param sid 连接id
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        this.sid = sid;
        webSocketSet.add(this);
        addOnlineCount();
        sendMessage("uid：" + this.sid + ", 连接成功");
        System.out.println("新的窗口 " + sid + " 已连接！当前在线人数为 " + getOnlineCount());

        //创建临时群
        joinGroup(sid, "999");
        System.out.println("群聊中有哪些用户："+groupChats);
        System.out.println("用户加入哪些群聊："+userGroups);

        try (Jedis jedis = new Jedis(host, port)) {
            jedis.auth(pwd);

            //根据 用户id查询改用户 离线信息
//            List<String> offlineMessages = jedis.lrange("offline_messages:" + sid, 0, -1);
//            //将信息展示给改用户
//            if (!offlineMessages.isEmpty()) {
//                for (String msg : offlineMessages) {
//                    sendMessage(msg);
//                }
//                jedis.del("offline_messages:" + sid);
//                System.out.println("已推送未读消息，并清除 Redis 记录：" + sid);
//            }

            //获取用户所在群聊的离线信息
            Set<String> groupIds = new HashSet<>(groupChats.keySet());
            for (String groupId : groupIds) {
                //获取群聊中所有成员id
                Set<String> members = groupChats.get(groupId);
                //展示群聊中成员的离线信息
                if (members != null && members.contains(sid)) {
                    List<String> groupMessages = jedis.lrange("offline_group_messages:" + groupId, 0, -1);
                    for (String msg : groupMessages) {
                            sendMessage(msg);
                    }
                    if (!groupMessages.isEmpty()) {
                        jedis.del("offline_group_messages:" + groupId);
                        System.out.println("已推送群聊 " + groupId + " 的未读消息，并清除 Redis 记录");
                    }
                }
            }
        }
    }

    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
        System.out.println("连接窗口" + sid + "关闭！当前在线人数为" + getOnlineCount());
    }

    /**
     * 获取客户端发送的信息 并解析
     * @param message 信息
     * @param session 会话对象
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            // 解析 JSON
            JSONObject jsonMessage = new JSONObject(message);
            //获取信息文本
            String content = jsonMessage.getString("content");

            // 1. 处理群聊消息（如果包含 groupId）
            if (jsonMessage.has("groupId") && !jsonMessage.getString("groupId").isEmpty()) {
                String groupId = jsonMessage.getString("groupId").replace(",", ""); // 去掉逗号
                System.out.println("群聊消息，groupId：" + groupId);

                sendGroupMessage(content, groupId, this.sid);
                return;
            }

            // 2. 处理私聊消息（如果包含 userId）
            if (jsonMessage.has("userId")) {
                JSONArray userIds = jsonMessage.getJSONArray("userId");
                for (int i = 0; i < userIds.length(); i++) {
                    String targetUserId = userIds.getString(i).replace(",", ""); // 去掉,
                    System.out.println("私聊消息，目标用户ID：" + targetUserId);
                    sendMessageToUsers(content, targetUserId, this.sid);
                }
            }

            System.out.println("服务器收到消息：" + content);

        } catch (Exception e) {
            System.out.println("消息解析失败：" + e.getMessage());
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 发送信息给客户端
     * @param message 信息
     */
    private void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送信息到个人 或 多发个人
     * @param message 信息
     * @param sid 要发送的用户
     * @param senderId 发送人
     */
    public static void sendMessageToUsers(String message, String sid, String senderId) {
        // 创建一个用于存储目标用户 SID 的集合
        Set<String> targetSids = new HashSet<>();
        // 如果 sid 不为空，则对其进行格式化并拆分为多个目标用户 SID
        if (sid != null && !sid.isEmpty()) {
            // 移除特殊字符和空白字符
            String sanitizedSid = sid.replaceAll("[\"'\\[\\]{}()（）“”‘’\\s]+", "");
            // 按逗号分割 SID，并存入目标 SID 集合
            String[] sidArray = sanitizedSid.split(",");
            targetSids.addAll(Arrays.asList(sidArray));
        }
        // 如果 sid 和 senderId 都为空，则广播消息给所有在线用户
        else if (sid == null && senderId == null) {
            for (SocketTest item : webSocketSet) {
                item.sendMessage(message);
            }
            return;
        }
        // 连接 Redis 进行离线消息存储
        try (Jedis jedis = new Jedis(host, port)) {
            jedis.auth(pwd); // 认证 Redis 连接
            // 遍历目标用户 SID 集合
            for (String targetSid : targetSids) {
                // 查找目标用户的 WebSocket 连接
                SocketTest recipient = findUserBySid(targetSid);
                if (recipient != null) {
                    // 用户在线，直接发送消息
                    recipient.sendMessage(message);
                } else {
                    // 用户不在线，将消息存入 Redis 作为离线消息
                    String sendTime = getCurrentTime(); // 获取当前发送时间
                    String msgJson;
                    // 判断消息类型（文本或图片）并构造 JSON 消息
                    if (isImage(message)) {
                        msgJson = "{\"sender\":\"" + senderId + "\", \"message\":\"" + message + "\", \"sendTime\":\"" + sendTime + "\", \"type\":\"image\"}";
                    } else {
                        msgJson = "{\"sender\":\"" + senderId + "\", \"message\":\"" + message + "\", \"sendTime\":\"" + sendTime + "\", \"type\":\"text\"}";
                    }
                    // 检查 Redis 中是否已存在相同的未读消息，避免重复存储
                    List<String> existingMessages = jedis.lrange("offline_messages:" + targetSid, 0, -1);
                    if (!existingMessages.contains(msgJson)) {
                        jedis.lpush("offline_messages:" + targetSid, msgJson); // 将消息存入 Redis
                        System.out.println("用户 " + targetSid + " 不在线，消息存入 Redis");
                    }
                }
            }
        }
    }


    /**
     * 发送信息到群聊
     * @param message 信息
     * @param groupId 群聊id
     * @param senderId 发送人id
     */
    private static void sendGroupMessage(String message, String groupId, String senderId) {
        // 获取群聊成员的 SID（用户唯一标识）
        Set<String> members = groupChats.get(groupId);
        if (members == null) {
            return; // 如果群聊不存在，则直接返回
        }
        System.out.println("发送群聊消息到群 " + groupId + " ：" + message);
        // 连接 Redis 进行离线消息存储
        try (Jedis jedis = new Jedis(host, port)) {
            jedis.auth(pwd); // 认证 Redis 连接
            // 遍历群聊成员
            for (String memberSid : members) {
                // 查找用户是否在线
                SocketTest recipient = findUserBySid(memberSid);
                if (recipient != null) {
                    // 用户在线，直接发送消息
                    recipient.sendMessage(message);
                } else {
                    // 用户不在线，将消息存入 Redis
                    String sendTime = getCurrentTime(); // 获取当前发送时间
                    String msgJson;
                    // 判断消息类型（文本或图片）并构造 JSON 消息
                    if (isImage(message)) {
                        msgJson = "{\"sender\":\"" + senderId + "\", \"message\":\"" + message + "\", \"sendTime\":\"" + sendTime + "\", \"type\":\"image\"}";
                    } else {
                        msgJson = "{\"sender\":\"" + senderId + "\", \"message\":\"" + message + "\", \"sendTime\":\"" + sendTime + "\", \"type\":\"text\"}";
                    }

                    // 检查 Redis 中是否已存在相同的未读消息，避免重复存储
                    List<String> existingMessages = jedis.lrange("offline_group_messages:" + groupId, 0, -1);
                    if (!existingMessages.contains(msgJson)) {
                        jedis.lpush("offline_group_messages:" + groupId, msgJson); // 存入 Redis
                        System.out.println("群聊 " + groupId + " 用户 " + memberSid + " 不在线，消息存入 Redis");
                    }
                }
            }
        }
    }


    /**
     * 查询用户是否在线
     * @param sid 用户id
     * @return socket
     */
    private static SocketTest findUserBySid(String sid) {
        for (SocketTest item : webSocketSet) {
            if (item.sid.equals(sid)) {
                return item;
            }
        }
        return null;
    }

    /**
     * 在set集合中新建成员
     * @param userId 成员id
     * @param groupId 群id
     */
    public static void joinGroup(String userId, String groupId) {
        groupChats.computeIfAbsent(groupId, k -> new HashSet<>()).add(userId);
        userGroups.computeIfAbsent(userId, k -> new HashSet<>()).add(groupId);
        System.out.println("用户 " + userId + " 加入群聊 " + groupId);
    }

    /**
     * 在set集合中移除成员
     * @param userId 成员id
     * @param groupId 群id
     */
    public static void leaveGroup(String userId, String groupId) {
        Set<String> members = groupChats.get(groupId);
        if (members != null) {
            members.remove(userId);
            if (members.isEmpty()) {
                groupChats.remove(groupId);
            }
            System.out.println("用户 " + userId + " 退出群聊 " + groupId);
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        SocketTest.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        SocketTest.onlineCount--;
    }
}
